#ifndef __LOG_H
#define __LOG_H

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <unistd.h>
#include <cstring>
#include <cstdarg>

#define SCREEN_TYPE 1
#define FILE_TYPE 2

// 日志等级
enum
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

// 根据等级返回对应字符串
std::string GradeToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "DEBUG";
    case INFO:
        return "INFO";
    case WARNING:
        return "WARNING";
    case ERROR:
        return "ERROR";
    case FATAL:
        return "FATAL";
    default:
        return "UNKNOWN";
    }
}

// 获取当前时间
std::string GetCurTime()
{
    const time_t cur_time = time(nullptr); // 获取当前时间戳
    struct tm *t = localtime(&cur_time);   // 将时间戳转换为tm结构体
    char time_buf[128];
    snprintf(time_buf, sizeof(time_buf), "%d-%02d-%02d %02d:%02d:%02d",
             t->tm_year + 1900,
             t->tm_mon + 1,
             t->tm_mday,
             t->tm_hour,
             t->tm_min,
             t->tm_sec);
    return time_buf;
}

struct log_message
{
    std::string level;
    pid_t _id;
    std::string _filename;
    int _fileline;
    std::string _cur_time;
    std::string _message_info;
};

pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;

class logger
{
public:
    logger(int type = SCREEN_TYPE, std::string filename = "./log.txt")
        : _type(type), _filename(filename)
    {
    }

    void ModifyTye(int type) // FILE or SCREEN
    {
        _type = type;
    }

    void logMessage(const std::string &filename, int fileline, int level, const char *format, ...)
    {
        log_message lg;
        lg.level = GradeToString(level);
        lg._filename = filename;
        lg._fileline = fileline;
        lg._cur_time = GetCurTime();
        lg._id = getpid();

        va_list va;
        va_start(va, format);
        char info_buf[1024];
        vsnprintf(info_buf, sizeof(info_buf), format, va);
        va_end(va);
        lg._message_info = info_buf;

        // 打印日志
        FlushLog(lg);
    }

    void FlushLog(log_message &lg)
    {
        pthread_mutex_lock(&_mutex);
        if (_type == SCREEN_TYPE)
            FlushLogScreen(lg);
        else
            FlushLogFILE(lg);
        pthread_mutex_unlock(&_mutex);
    }

    void FlushLogScreen(log_message &lg)
    {
        printf("[%s][%d][%s][%d][%s]: %s\n", lg.level.c_str(), lg._id, lg._filename.c_str(), lg._fileline, lg._cur_time.c_str(), lg._message_info.c_str());
    }

    void FlushLogFILE(log_message &lg)
    {
        std::ofstream out(_filename, std::ios::app);
        if (!out.is_open())
            return;

        char log_buf[1024];
        snprintf(log_buf, sizeof(log_buf), "[%s][%d][%s][%d][%s]: %s\n", lg.level.c_str(), lg._id, lg._filename.c_str(), lg._fileline, lg._cur_time.c_str(), lg._message_info.c_str());
        out.write(log_buf, strlen(log_buf));
        out.close();
    }

private:
    int _type;
    std::string _filename;
};

logger lg;

#define EnableScreen() lg.ModifyTye(SCREEN_TYPE)
#define EnableFile() lg.ModifyTye(FILE_TYPE)

#define LOG(level, format, ...) lg.logMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__)

#endif